/*                                  ATELIER                                   */

/* %%LICENSE_TAG%%                                                            */
package edu.gmu.view.cube;

import edu.gmu.atelier.RealVector;
import edu.gmu.atelier.Vector;
import java.awt.Graphics2D;
import javax.swing.JComponent;

import edu.gmu.view.canvas.AbstractLayer;
import java.awt.Color;
import java.awt.geom.AffineTransform;

/*******************************************************************
 * Draws a cube in various rotations/positions.
 * @author  James H. Pope
 * @version $Id:$
 *******************************************************************/
public class CubeLayer extends AbstractLayer
{
    /**
     * model
     */
    private ControlModel controlModel = null;

    /**
     * Defines the current position of the cube - used to translate
     */
    private Vector positionVector = null;
    private double cubeSize = 100.0;
    private double cubeHalf = 0.5*cubeSize;
    
    
    /**
     * Creates a new CubeLayer with specified parameters.
     * @param controlModel
     */
    public CubeLayer ( ControlModel controlModel )
    {
        this.controlModel = controlModel;
        
        this.positionVector = new RealVector( 0.0, 0.0, -(controlModel.getZNear()+cubeHalf) );
    }
    
    /**
     * Gets the controlModel
     * @param controlModel
     */
    public void setControlModel( ControlModel controlModel )
    {
        this.controlModel = controlModel;
    }
    
    /**
     * Sets the controlModel
     * @return controlModel
     */
    public ControlModel getControlModel()
    {
        return this.controlModel;
    }
    
    
    /*********************************************************************
     * Paints images on specified Component object using passed Graphics.
     *
     * @param component 
     * @param graphics
     *********************************************************************/
    public void paintLayer( JComponent  component, Graphics2D  graphics )
    {
        AffineTransform originalTransform = graphics.getTransform();

        int width  = component.getWidth();
        int height = component.getHeight();
        
        double znear = this.controlModel.getZNear();
        double zfar  = this.controlModel.getZFar();
        
        
        /*
         * Orient from default Java2D (upper/left) to math (lower/left)
         */
        graphics.translate( 0 , component.getHeight() ); // translate
        graphics.scale(1, -1); // flip


        //--------------------------------------------------------------------//
        // From here forward all graphics transformations are custom code
        // no swing/awt/etc. This should be similar to OpenGL code and the like.
        // Make sure "graphics.methods" are NOT used, Pen/Transform do the
        // graphics and mixing the two will be grossly incorrect.
        //--------------------------------------------------------------------//
        Pen pen = new Pen( graphics, component );
        double aspect = width/height;
        //pen.setPerspective(Math.PI/8, aspect, 900.0, 1800.0);
        // Note: Nothing to do with the camera, defines the volume for clip.view
        pen.setPerspective(Math.PI/4, aspect, znear, zfar);
        Transform modelView = pen.getModelView();

        modelView.push();

        double pz = this.positionVector.getElement(Pen.Z).doubleValue();
        //System.out.println( "z="+pz );
        
        Vector velocityVector    = this.controlModel.getVelocityVector();
        
        /*
         * Note that zfar and znear are stored as positive numbers and the
         * setPerspective requires them to be positive (legacy opengl).
         * However the perspective is ALWAYS 0 < z < -infinity, i.e. negative
         * Intuition is your "eye" is at z=0 looking "down/away" along z-axis
         * Any positive z is "behind" you (your eye) and will not be visible.
         * Internally the projection treats them as if negative.
         * Thus near=1 and far=10 is actually near=-1 and far=-10
         */
        if( pz < -zfar || pz > -znear )
        {
            // Switch directions
            //System.out.println( "    switching direction." );
            double zDir = -velocityVector.getElement(Pen.Z).doubleValue();
            velocityVector = new RealVector( 0.0, 0.0, zDir );
            this.controlModel.setVelocityVector(velocityVector);
        }
        
        /*
         * "This moves the cube along the z axis at "velocity" of vector".
         */
        this.positionVector = this.positionVector.add(velocityVector);
        
        /*
         * Cube "rests" centered at 0 and unit size, need to
         * 1. Translate position
         * 2. Apply any rotations
         * 3. Scale appropriately
         */
        modelView.translate(    this.positionVector.getElement(Pen.X).doubleValue(),
                                this.positionVector.getElement(Pen.Y).doubleValue(),
                                this.positionVector.getElement(Pen.Z).doubleValue() );
        //transform.translate( 0.5*width, 0.5*height, 0);

        //--------------------------------------------------------------------//
        // Apply user provided translate, rotate, and scale transforms
        //--------------------------------------------------------------------//
        double xRotation = this.controlModel.getXRotation();
        double yRotation = this.controlModel.getYRotation();
        double zRotation = this.controlModel.getZRotation();
        
        double xTranslate = this.controlModel.getXTranslate();
        double yTranslate = this.controlModel.getYTranslate();
        double zTranslate = this.controlModel.getZTranslate();
        
        double xScaling = this.controlModel.getXScaling();
        double yScaling = this.controlModel.getYScaling();
        double zScaling = this.controlModel.getZScaling();
        
        modelView.rotateX( xRotation );
        modelView.rotateY( yRotation );
        modelView.rotateZ( zRotation );
        
        modelView.translate(xTranslate, yTranslate, zTranslate);
        modelView.scale( xScaling, yScaling, zScaling );
        //--------------------------------------------------------------------//
        
        /*
         * The objects may be moving. Of course, in real life both may be 
         * moving (i.e. Cube rotates and camera translates back).  If an
         * object moves forward and so does the camera, then the graphic
         * should remain unchanged (old physics question of who is moving).
         */
        
        /*
         * Finally apply the transformations to the cube and display
         * We draw it from four different angles.
         */
        Cube cube = new Cube(this.cubeSize);
        pen.setColor( Color.BLUE );
        
        Transform projection = pen.getProjection();
        
        projection.push();
        //pen.setLookAt(0, 0, 0,   0, 0, 0,   0, 1, 0);
        pen.setViewport(0, 0, component.getWidth()/2, component.getHeight()/2);
        // implicitly camera eye at (0,0,0) looking down -z
        cube.draw(pen);
        projection.pop();
        
        projection.push();
        projection.setLookAt(500, 0, -500,  0, 0, -500,   0, 1, 0);
        pen.setViewport(component.getWidth()/2, component.getHeight()/2, component.getWidth()/2, component.getHeight()/2);
        cube.draw(pen);
        projection.pop();
        
        projection.push();
        projection.setLookAt(-500, 0, -500,   0, 0, -500,   0, 1, 0);
        pen.setViewport(component.getWidth()/2, 0, component.getWidth()/2, component.getHeight()/2);
        cube.draw(pen);
        projection.pop();
        
        projection.push();
        //projection.setLookAt(0, 500, -500,   0, 0, -500,   0, 1, 0); // NOT WORKING
        pen.setViewport(0, component.getHeight()/2, component.getWidth()/2, component.getHeight()/2);
        cube.draw(pen);
        projection.pop();
        
        modelView.pop();
        //--------------------------------------------------------------------//

        // Return swing to previous state, though not sure anything changed?
        graphics.setTransform(originalTransform);
    }
     
}

/*                                  ATELIER                                   */
